/* -*- c++ -*- */
/* 
 * Copyright 2013 <+YOU OR YOUR COMPANY+>.
 * 
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <gr_io_signature.h>
#include "protocol_source_impl.h"

namespace gr {
  namespace blocks {

    protocol_source::sptr
    protocol_source::make(
        int nb_constellation_points,
        int skip_len,
	    const std::vector<int> &key_seq,
	    const std::vector<int> &snr_estimation_seq) 
    {
      return gnuradio::get_initial_sptr (new protocol_source_impl(
        nb_constellation_points, skip_len, key_seq, snr_estimation_seq));
    }

    /*
     * The private constructor
     */
    protocol_source_impl::protocol_source_impl(
        int nb_constellation_points,
        int skip_len,
	    const std::vector<int> &key_seq,
	    const std::vector<int> &snr_estimation_seq)
      : gr_sync_block("protocol_source",
              gr_make_io_signature(1, 1, sizeof (char)),
              gr_make_io_signature(1, 1, sizeof (char))),
      d_state(protocol_phases::SKIP_FIRST),
      d_snr_estimation_seq(snr_estimation_seq),
      d_snr_estimation_length(snr_estimation_seq.size()),
      d_key_seq(key_seq),
      d_key_length(key_seq.size()),
      d_symbol_counter(0),
      d_nb_const_points(nb_constellation_points),
      d_skip_len(skip_len),
      d_last_sym(0)
    {}

    /*
     * Our virtual destructor.
     */
    protocol_source_impl::~protocol_source_impl()
    {
    }

    int
    protocol_source_impl::work(int noutput_items,
              gr_vector_const_void_star &input_items,
              gr_vector_void_star &output_items)
    {
        const char *in = (const char *) input_items[0];
        char *out = (char *) output_items[0];
        unsigned int i;
        char sym;
        bool d_is_differential = true;


// TODO: receiv d_skip_len
        switch(d_state) {
        case protocol_phases::SKIP_FIRST:
            i = 0;
            while ((d_symbol_counter < noutput_items) && (d_symbol_counter < d_skip_len)){
                out[i] = rand()%d_nb_const_points;
                d_symbol_counter ++;
                i ++;
            }

            if (d_symbol_counter == d_skip_len) {
                //printf("\t\t Source skip: %d %d %d\n",i, d_symbol_counter, noutput_items);
                d_symbol_counter = 0;
                d_state = protocol_phases::SEND_KEY;
            }

            consume_each(0);
            produce(0, i);
            break;

        case protocol_phases::SEND_KEY:
            i = 0;
            while ((d_symbol_counter < noutput_items) && (d_symbol_counter < d_key_length)){
                sym = d_key_seq[d_symbol_counter];
                // TODO: put the differential in class constructor
                if(d_is_differential){
                    out[i] = (sym + d_last_sym) % d_nb_const_points;
                    d_last_sym = out[i];
                }else{
                    out[i] = sym;
                }

                //printf("sending key: input: %d, output %d\n\n", sym, out[i]);

                //printf("\t\t Source key: %d %d %d %d\n",i, out[i], d_key_seq[i], d_symbol_counter+d_skip_len);
                d_symbol_counter ++;
                i ++;
            }
        
            if (d_symbol_counter == d_key_length) {
                d_symbol_counter = 0;
                d_state = protocol_phases::SNR_ESTIMATION;
                d_last_sym = 0;
            }
    
            if(i){
                //printf("\t\t PS: produce %d\n", i);
            }
            consume_each(0);
            produce(0, i);
            break;

        case protocol_phases::SNR_ESTIMATION:
            i = 0;
            //printf("Protocol Source:");
            while ((i < noutput_items) && (d_symbol_counter < d_snr_estimation_length)){
                //next = 2*d_lfsr.next_bit() + d_lfsr.next_bit();
                sym = d_snr_estimation_seq[d_symbol_counter];
                // TODO: put the differential in class constructor
                if(true){
                    out[i] = (sym + d_last_sym) % d_nb_const_points;
                    d_last_sym = out[i];
                }else{
                    out[i] = sym;
                }
                d_symbol_counter ++;
                //printf("%d ",out[i]);
                i ++;
            }
            //printf("\nProtocol Source (%d/%d) Sended %d\n", d_symbol_counter, d_snr_estimation_length, i);
        
            if (d_symbol_counter == d_snr_estimation_length) {
                //printf("Source: %d, Finish SNR estimation\n", d_symbol_counter);
                d_symbol_counter = 0;
                d_state = protocol_phases::SEND_DATA;
            }

            consume_each(0);
            produce(0, i);
            break;
        
        case protocol_phases::SEND_DATA:
            //printf("Sink: sending %d items ///\n", noutput_items);
            for(i=0; i<noutput_items; i++){
                //printf("in[%d]=%d  ", i, in[i]);
                out[i] = in[i];
            }
            //printf("///\n");
            consume_each(noutput_items);
            produce(0, noutput_items);
            break;
        }
        
        return WORK_CALLED_PRODUCE;
    }

  } /* namespace blocks */
} /* namespace gr */

